﻿namespace TestTool.HttpTransport
{
    using System;
    using System.Threading;

    internal abstract class AsyncResult : IAsyncResult
    {
        private AsyncCallback _callback;
        private bool _completedSynchronously;
        private bool _endCalled;
        private Exception _exception;
        private bool _isCompleted;
        private ManualResetEvent _manualResetEvent;
        private object _state;
        private object _thisLock;

        protected AsyncResult(AsyncCallback callback, object state)
        {
            this._callback = callback;
            this._state = state;
            this._thisLock = new object();
        }

        protected void Complete(bool completedSynchronously)
        {
            if (this._isCompleted)
            {
                throw new InvalidOperationException("Cannot call Complete twice");
            }
            this._completedSynchronously = completedSynchronously;
            if (completedSynchronously)
            {
                this._isCompleted = true;
            }
            else
            {
                lock (this.ThisLock)
                {
                    this._isCompleted = true;
                    if (this._manualResetEvent != null)
                    {
                        this._manualResetEvent.Set();
                    }
                }
            }
            if (this._callback != null)
            {
                this._callback(this);
            }
        }

        protected void Complete(bool completedSynchronously, Exception exception)
        {
            this._exception = exception;
            this.Complete(completedSynchronously);
        }

        protected static TAsyncResult End<TAsyncResult>(IAsyncResult result) where TAsyncResult: AsyncResult
        {
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }
            TAsyncResult local = result as TAsyncResult;
            if (local == null)
            {
                throw new ArgumentException("Invalid async result.", "result");
            }
            if (local._endCalled)
            {
                throw new InvalidOperationException("Async object already ended.");
            }
            local._endCalled = true;
            if (!local._isCompleted)
            {
                local.AsyncWaitHandle.WaitOne();
            }
            if (local._manualResetEvent != null)
            {
                local._manualResetEvent.Close();
            }
            if (local._exception != null)
            {
                throw local._exception;
            }
            return local;
        }

        public object AsyncState
        {
            get
            {
                return this._state;
            }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (this._manualResetEvent == null)
                {
                    lock (this.ThisLock)
                    {
                        if (this._manualResetEvent == null)
                        {
                            this._manualResetEvent = new ManualResetEvent(this._isCompleted);
                        }
                    }
                }
                return this._manualResetEvent;
            }
        }

        public bool CompletedSynchronously
        {
            get
            {
                return this._completedSynchronously;
            }
        }

        public bool IsCompleted
        {
            get
            {
                return this._isCompleted;
            }
        }

        private object ThisLock
        {
            get
            {
                return this._thisLock;
            }
        }
    }
}

